
#ifndef __TCP_SERVER_H
#define __TCP_SERVER_H

#include <openssl/err.h>
#include <apr-1/apr.h>
#include <apr-1/apr_atomic.h>
#include <apr-1/apr_file_io.h>
#include <apr-1/apr_poll.h>
#include <apr-1/apr_portable.h>
#include "../aprtools/APRMutex.h"
#include "../aprtools/APRBucketBrigade.h"
#include "netgeneral.h"

#define PENDING_CONNECTIONS 8

#define SERVERSTAGE_INACTIVE   0
#define SERVERSTAGE_RECEIVING  1
#define SERVERSTAGE_READING    2
#define SERVERSTAGE_WRITING    3
#define SERVERSTAGE_SENDING    4

// 0->1 ServerListener
// 1->2 Session
// 2->3 Session
// 3->4 Session
// 4->0 ServerListener

/** A struct shared between threads, a collection of pointers to pipes which
 * allow communication between the listening server side and the client
 * handlers. The pointers on the pipes are NULL on an inactive, waiting
 * listening thread. */
typedef struct clientpipes
{
   /** Integer flag denoting the stage of the connection on this thread. */
   apr_uint32_t stage;
   /** The memory pool local to this thread. */
   apr_pool_t *localmp;
   /** The socket address associated with the client connected on this
    * thread. */
   apr_sockaddr_t *cliaddr;
   /** The pipe from the listening socket to the serving thread. */
   APRBucketBrigade *recv_to_handler;
   /** The pipe from the serving thread back to the listening thread. */
   APRBucketBrigade *handler_to_send;
   /** Client socket pointer. */
   apr_socket_t *clisock;
   /** Mutex for starting sessions. */
   APRMutex *sessionstart;
   /** Mutex to wait on when the thread is inactive. */
   APRMutex *waiting;
} ClientPipes;

/** A functor for generating the thread function in multiple threads. */
typedef void *(*functor)(apr_thread_t *, void *);

/** A basic TCP server class. This is the base class for all types of 
 * servers. */
class TCPServer
{
   public:
      TCPServer(int inoofthreads, int iportno, const char *ihostname, apr_pool_t *upperpool);
      virtual ~TCPServer();
      int StartListeningThread();
      int StartSessionThreads();
      void SocketAccept(apr_pollset_t *pollset, apr_socket_t *lsock, apr_pool_t *mp);
      static void *ServerListen(apr_thread_t *tthread, void *vthis);
      static void *ClientHandler(apr_thread_t *tthread, void *args);
      /** Get the client handler as a function pointer. */
      virtual functor GetClientHandlerFunction() const { return (functor)(&ClientHandler); }
      int JoinThreads();
      apr_socket_t *CreateListeningSocket(apr_pool_t *mp);
      const char *StaticStringGetClientAddress(int threadid);
      /** Is the TCP server listening? */
      int IsListening() { return (!listenstop); }
      /** Pointer to an array of client pipes. */
      ClientPipes *pipearray;
      /** The one listening thread. */
      apr_thread_t *listening_thread;
      /** The many per-session client threads. */
      apr_thread_t **session_threads;
      /** The thread attributes for the one listening thread. */
      apr_threadattr_t *listening_attr;
      /** The many thread attributes for the many client threads. */
      apr_threadattr_t **session_threadattrs;
      /** The memory pool. */
      apr_pool_t *mp;
      /** The number of client threads. */
      int noofthreads;
   private:
      /** Hostname for the listening server. */
      char *hostname;
      /** Port number for the listening server. */
      int portno;
      /** The time out for the listening server. */
      int timeout;
      /** Flag to be set by the listen to stop. */
      int listenstop;
   protected:
      /** Flag to be set by the server */
      int sessionstop;
};

#endif
